﻿using System;
using System.Collections.Generic;
using System.Text;
using DBSourceToolsLib.SysObjects;

namespace DBSourceToolsLib
{
    public class TargetDatabase : DatabaseEx
    {
        private SourceDatabase m_SourceDatabase;
        private List<SOPatch> m_PatchList = new List<SOPatch>();
        public List<SOPatch> PatchList
        {
            get { return m_PatchList; }
        }


        public TargetDatabase(DBConnectionLib.Database oDatabase, SourceDatabase oSourceDatabase)
            : base(oDatabase, true)
        {
            this.m_SourceDatabase = oSourceDatabase;
            this.m_BaseDirectory = oSourceDatabase.BaseDirectory + "\\DeploymentTargets\\" + oDatabase.NickName;
            FileUtils oFileUtils = new FileUtils();
            oFileUtils.EnsureDirectoryExists(this.m_BaseDirectory + "\\Patches");
            // load patches.
            
            LoadPatchesFromDisk();


        }

        public void WriteCreateScript(DatabaseProject oProject, EventHandler<ProgressEventArgs> localHandler,
            bool m_bIsZipFile)
        {
            // TODO : use utf-8 option for sqlcmd (albToxto)
            // TODO : use relative paths only

            string strBaseBinDir = UserSettings.Instance().StartupPath;
            FileUtils.EnsureFileIsWriteable(this.BaseDirectory + "\\Run_Create_Scripts.bat");

            using (System.IO.StreamWriter oStreamWriter = new System.IO.StreamWriter(
                this.BaseDirectory + "\\Run_Create_Scripts.bat"))
            {

                string strDB_BASE_DIR = m_SourceDatabase.BaseDirectory;
                string strBASE_BIN_DIR = strBaseBinDir;
                string strPROJECT_BASE_DIR = oProject.BaseDirectory;
                string strPATCH_DIR = this.BaseDirectory;
                // write settings
                string strSQLCMD = (UserSettings.Instance().UseIsql ? "isql " : "sqlcmd ");

                if (m_bIsZipFile)
                {
                    strDB_BASE_DIR = "..\\..";
                    strBASE_BIN_DIR = "..\\..\\..\\bin";
                    strPROJECT_BASE_DIR = "..\\..\\..";
                    strPATCH_DIR = strPROJECT_BASE_DIR;
                }
                oStreamWriter.WriteLine("set DB_BASE_DIR=" + strDB_BASE_DIR + "\\");
                oStreamWriter.WriteLine("set BASE_BIN_DIR=" + strBASE_BIN_DIR + "\\");
                oStreamWriter.WriteLine("set PROJECT_BASE_DIR=" + strPROJECT_BASE_DIR + "\\");
                oStreamWriter.WriteLine("set PATCH_DIR=" + strPATCH_DIR + "\\");
                oStreamWriter.WriteLine("rem");

                string strLoginOptions = " -E ";
                if (!string.IsNullOrEmpty(this.UserName))
                {
                    strLoginOptions = " -U " + this.UserName + " -P " + this.Password;
                }

                if (m_SourceDatabase.ScriptOptions.ScriptDropAndCreate)
                {
                    // first write this parent's drop and create scripts
                    string strDropScript = strSQLCMD + " -S " + this.DataSourceName + strLoginOptions + " -i \"" + this.DropScript.ScriptFilePath + "\"";
                    strDropScript = strDropScript.Replace(m_SourceDatabase.BaseDirectory + "\\", "%DB_BASE_DIR%");
                    oStreamWriter.WriteLine(strDropScript);
                    string strCreateScript = strSQLCMD + " -S " + this.DataSourceName + strLoginOptions + " -i \"" + this.CreateScript.ScriptFilePath + "\"";
                    strCreateScript = strCreateScript.Replace(m_SourceDatabase.BaseDirectory + "\\", "%DB_BASE_DIR%");
                    oStreamWriter.WriteLine(strCreateScript);
                }


                // schemas are not included in the dependency tree
                foreach (SOSchema oSchema in m_SourceDatabase.SysObjects.Schemas)
                {
                    oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSchema.ShortFileName + "\"");
                }
                // xmlschemas are not included in the dependency tree
                foreach (SOXmlSchema oSchema in m_SourceDatabase.SysObjects.XmlSchemas)
                {
                    oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSchema.ShortFileName + "\"");
                }
                
                // roles are not included in the dependency tree
                foreach (SORole oRole in m_SourceDatabase.SysObjects.Roles)
                {
                    oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oRole.ShortFileName + "\"");
                }

                // xmlschemas are not included in the dependency tree
                foreach (SOUser oUser in m_SourceDatabase.SysObjects.Users)
                {
                    if (m_SourceDatabase.ScriptOptions.ScriptUsers &&
                        m_SourceDatabase.ScriptOptions.MustScriptUser(oUser.Name))
                    {
                        oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oUser.ShortFileName + "\"");
                    }
                }
                // create tables first, then stored procs.
                // tables first
                foreach (SysObject oSysObject in m_SourceDatabase.SysObjects.SODependencyTree.DependencyTree)
                {
                    if (oSysObject == null)
                        continue;
                    switch (oSysObject.Type)
                    {
                        case SysObjectType.View:
                            if (m_SourceDatabase.ScriptOptions.ScriptViews &&
                                m_SourceDatabase.ScriptOptions.MustScriptView(oSysObject.Name))
                            {
                                oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSysObject.ShortFileName + "\"");
                            }
                            break;
                        case SysObjectType.Table:

                            

                            if (m_SourceDatabase.ScriptOptions.ScriptTables &&
                                m_SourceDatabase.ScriptOptions.MustScriptTable(oSysObject.Name))
                            {
                                oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSysObject.ShortFileName + "\"");
                            }
                            if (m_SourceDatabase.ScriptOptions.ScriptData &&
                                m_SourceDatabase.ScriptOptions.MustScriptData(oSysObject.Name))
                            {
                                oStreamWriter.WriteLine("\"%BASE_BIN_DIR%"
                                    + "DBSourceDataLoader.exe\" "
                                    + "%PROJECT_BASE_DIR%" + " "
                                    + m_SourceDatabase.NickName + " "
                                    + this.NickName + " \"" + oSysObject.Name + "\" "
                                    + "\"" 
                                    +(m_bIsZipFile ? "%DB_BASE_DIR%\\..\\" : "%PROJECT_BASE_DIR%\\")
                                    + oProject.ProjectName + ".xml" + "\"");

                            }
                            break;
                        case SysObjectType.Proc:
                            //if (m_SourceDatabase.ScriptOptions.ScriptStoredProcs &&
                            //    m_SourceDatabase.ScriptOptions.MustScriptProc(oSysObject.Name))
                            //{
                            //    oStreamWriter.WriteLine("sqlcmd -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSysObject.ShortFileName + "\"");
                            //}
                            // do nothing
                            break;
                        default:
                            oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSysObject.ShortFileName + "\"");
                            break;
                    }
                }
                foreach (SysObject oSysObject in m_SourceDatabase.SysObjects.SODependencyTree.DependencyTree)
                {
                    if (oSysObject == null)
                        continue;
                    switch (oSysObject.Type)
                    {
                        case SysObjectType.Proc:
                            if (m_SourceDatabase.ScriptOptions.ScriptStoredProcs &&
                                m_SourceDatabase.ScriptOptions.MustScriptProc(oSysObject.Name))
                            {
                                oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oSysObject.ShortFileName + "\"");
                            }
                            break;
                    }
                }


 

                // triggers are not included in the dependency tree
                foreach (SOTrigger oTrigger in m_SourceDatabase.SysObjects.Triggers)
                {
                    oStreamWriter.WriteLine(strSQLCMD + " -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%DB_BASE_DIR%" + oTrigger.ShortFileName + "\"");
                }

                foreach (SOPatch oPatch in m_PatchList)
                {
                    oStreamWriter.WriteLine(strSQLCMD + " -f 850 -S " + this.DataSourceName + " -d " + this.DatabaseName + strLoginOptions + " -i \"%PATCH_DIR%" + oPatch.ShortFileName + "\"");
                }

            }


        }



        public void LoadPatchesFromDisk()
        {
            m_PatchList.Clear();
            System.IO.DirectoryInfo baseDirInfo = new System.IO.DirectoryInfo(this.m_BaseDirectory + "\\Patches");
            foreach (System.IO.FileInfo oFile in baseDirInfo.GetFiles())
            {
                if (oFile.Extension.ToLower().Equals(".sql"))
                    m_PatchList.Add(new SOPatch(oFile.FullName, this));
                //System.IO.File.Delete(oFile.FullName);
            }

        }

        public string NewPatch(string strFileName)
        {
            string strFullPatchName = this.m_BaseDirectory + "\\Patches\\" + strFileName;
            
            FileUtils oFileUtils = new FileUtils();
            oFileUtils.EnsureFileDirectoryExists(strFullPatchName);

            if (!System.IO.File.Exists(strFullPatchName))
            {
                using (System.IO.StreamWriter oWriter = new System.IO.StreamWriter(strFullPatchName))
                {
                    oWriter.WriteLine("-- " + strFileName);
                }
            }

            LoadPatchesFromDisk();
            return strFullPatchName;
        }

        internal Composite.IDBComposite GetDBComposite()
        {
            Composite.DBComposite oDBComposite = new Composite.DBComposite(this.NickName,this.NickName,
                Composite.DBCompositeType.Deployment_Target,
                new Composite.TreeNodeTag(m_SourceDatabase.NickName, "Deployment Target", this.NickName, null));

            oDBComposite.Add(new Composite.DBComposite("Run_Create_Scripts.bat","Run_create",
                Composite.DBCompositeType.File,
                new Composite.TreeNodeTag(m_SourceDatabase.NickName, "File", BaseDirectory + "\\Run_Create_Scripts.bat", null)));

            oDBComposite.Add(new Composite.DBComposite("CreateDB.sql","Create",
                Composite.DBCompositeType.File,
                new Composite.TreeNodeTag(m_SourceDatabase.NickName, "File", CreateScript.ScriptFilePath, null)));

            oDBComposite.Add(new Composite.DBComposite("DropDB.sql","Drop",
                Composite.DBCompositeType.File,
                new Composite.TreeNodeTag(m_SourceDatabase.NickName, "File", DropScript.ScriptFilePath, null)));

            Composite.DBComposite oPatchComposite = new Composite.DBComposite("Patches","Patches",
                Composite.DBCompositeType.Patches,
                new Composite.TreeNodeTag(m_SourceDatabase.NickName, "Patches", this.NickName, null));

            foreach (SOPatch oPatch in this.PatchList)
                oPatchComposite.Add(oPatch.GetDBComposite());

            oDBComposite.Add(oPatchComposite);

            return oDBComposite;
        }
    }
}
